package tech.yixiyun.framework.kuafu.boot.server;


import tech.yixiyun.framework.kuafu.config.AppConfig;
import tech.yixiyun.framework.kuafu.config.ConfigKey;
import tech.yixiyun.framework.kuafu.controller.action.Action;
import tech.yixiyun.framework.kuafu.controller.action.ActionContext;
import tech.yixiyun.framework.kuafu.controller.action.ActionDoneRegistry;
import tech.yixiyun.framework.kuafu.controller.action.ActionInvoker;
import tech.yixiyun.framework.kuafu.controller.request.KuafuRequest;
import tech.yixiyun.framework.kuafu.controller.response.KuafuResponse;
import tech.yixiyun.framework.kuafu.controller.route.RouteContext;
import tech.yixiyun.framework.kuafu.controller.route.RouteNode;
import tech.yixiyun.framework.kuafu.kits.HttpKit;
import tech.yixiyun.framework.kuafu.log.LOGGER;
import tech.yixiyun.framework.kuafu.shutdown.ShutdownRegistry;
import tech.yixiyun.framework.kuafu.view.View;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import java.util.Objects;

/**
 * @author Boolone
 * @date 2019/12/4
 */

public class MainContextFilter implements Filter {

    private List<String> excludePaths;

    @Override
    public void init(FilterConfig config) throws ServletException {
        excludePaths = AppConfig.getAsStringList(ConfigKey.REQUEST_RESOURCES_EXCLUDE);
    }


    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        //如果是从 Dispatcher 转向过来的请求，接着复用
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        boolean ifcontinue = processReqRes(request, response);

        if (ifcontinue == false) return;

        RouteNode route = resolveRoute(request);

        View view = null;
        try {
            if (route == null) {
                filterChain.doFilter(request, response);
            } else {
                KuafuRequest req = servletRequest instanceof KuafuRequest ? (KuafuRequest) servletRequest : new KuafuRequest(request);
                KuafuResponse res = servletResponse instanceof KuafuResponse ? (KuafuResponse) servletResponse : new KuafuResponse(response);
                Action action = new Action(route, req, res);
                ActionContext.register(action);

                view = new ActionInvoker(action).invoke();
                if (view != null) {
                    view.render();
                }
            }
        } catch (Exception e) {
            if (e instanceof RuntimeException) {
                throw (RuntimeException)e;
            }
            throw new RuntimeException(e);
//            LOGGER.error(e);
        } finally {
            ActionDoneRegistry.execute();
        }

    }


    /**
     * 解析路由
     * @param request
     * @return
     */
    private RouteNode resolveRoute(HttpServletRequest request) {
        String uri = request.getRequestURI();
        //如果请求了 /aa/，需要把最后的/去掉去匹配
        if (uri.length() > 1 && uri.endsWith("/")) {
            uri = uri.substring(0, uri.length() - 1);
        }
        if (isExcludePath(uri)) {
            return null;
        }
        String contextPath = AppConfig.getAsString(ConfigKey.SERVER_CONTEXTPATH);
        if ("/".equals(contextPath) == false) {
            uri = uri.replaceFirst(contextPath, "");
        }
        if (uri.equals("")) {
            uri = RouteNode.ROOT_PATH;
        }
        LOGGER.info("[uri]{}  [from]{}" , uri, HttpKit.getIPAddress(request));
        RouteNode route = RouteContext.getRoute(uri);
        if (route == null) { //全路径没匹配到，就降级处理，比如 /a/1-2 没匹配到，就尝试 /a，将1-2作为参数传进去
            //这里有个特殊情况 ：在没有配置exclude的情况下，/a.html 是无法分清楚要访问的是 / 并且传参a.html，还是就要访问/a.html
            //所以这里针对 / 做下特殊处理，如果有扩展符，就认为是后一种情况，直接跳过让容器处理，没有就是前一种情况
            if (uri.lastIndexOf(RouteNode.ROOT_PATH) == 0) {
                if (uri.contains(".")) {
                    //这里不做任何处理
                } else {
                    route = RouteContext.getRoute(RouteNode.ROOT_PATH);
                }
            } else {
                uri = uri.substring(0, uri.lastIndexOf(RouteNode.ROOT_PATH));
                route = RouteContext.getRoute(uri);
            }
        }
        return route;
    }



    /**
     * 看看这个请求是否需要处理
     * @param request
     * @param response
     * @return
     * @throws IOException
     */
    private boolean processReqRes(HttpServletRequest request, HttpServletResponse response) throws IOException {
        request.setCharacterEncoding("UTF-8");
        response.setCharacterEncoding("UTF-8");

        /**
         * 根据是否开启跨域支持，来对请求响应做处理
         */
        if (Objects.equals(true, AppConfig.getAsBoolean(ConfigKey.REQUEST_CORS))) {
            response.setHeader("Access-Control-Allow-Origin", "*");

            /* 允许跨域的请求方法GET, POST, HEAD 等 */
            response.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS, PUT, DELETE");
            /* 重新预检验跨域的缓存时间 (s) */
            response.setHeader("Access-Control-Max-Age", "3600");
            /* 允许跨域的请求头 */
            response.setHeader("Access-Control-Allow-Headers", "*");
            //跨域可能有预检请求，method是options,这种请求直接过滤
            if (request.getMethod().equalsIgnoreCase("options")) {
                response.getWriter().write("");
                return false;
            }
        }
        return true;
    }

    /**
     * 是否是被排除的请求
     * @param uri
     * @return
     */
    private boolean isExcludePath(String uri) {
        if (excludePaths.isEmpty()) return false;
        uri = uri.toLowerCase();
        for (String excludePath : excludePaths) {
            excludePath = excludePath.toLowerCase();
            if (excludePath.equals("*.*")) {
                if (uri.indexOf(".") != -1) {
                    return true;
                }
            }
            if (excludePath.startsWith("*.")) { //扩展匹配
                if (uri.endsWith(excludePath.substring(1))) {
                    return true;
                }
            } else if (excludePath.endsWith("/*")) { //前缀匹配
                if (uri.startsWith(excludePath.substring(0, excludePath.length()-1))) {
                    return true;
                }
            } else { //全路径匹配
                if (uri.equals(excludePath)) {
                    return true;
                }
            }
        }
        return false;
    }

    @Override
    public void destroy() {
        LOGGER.warnTitle("应用关闭中");
        ShutdownRegistry.execute();
    }
}
